#pragma once

//
class Window //base object
{

   WNDCLASSEXW tempwce; PAINTSTRUCT ps; int r;

public:

   HWND hwnd; HINSTANCE hinst; HWND hpar; int hid; Window* hpobj; wchar_t buf[333];

   void Create(Window* pobj, DWORD wces, DWORD wes, DWORD ws, wchar_t* cname, int x, int y, int w, int h, HWND par, int id);

   LONG_PTR SendMsg(UINT m, WPARAM wp, LPARAM lp) { return SendMessageW(hwnd, m, wp, lp); }
   LONG_PTR SendMsgP(UINT m, WPARAM wp, LPARAM lp) { return SendMessageW(hpar, m, wp, lp); }
   LONG_PTR SendDlgMsg(HWND d, int id, UINT m, WPARAM wp, LPARAM lp) { return SendDlgItemMessageW(d, id, m, wp, lp); }

   void msg(const wchar_t* m) { MessageBoxW(GetActiveWindow(), m, L"msg", MB_ICONEXCLAMATION); }
   void err(const char* m) { MessageBoxA(GetActiveWindow(), m, "err", MB_ICONERROR); }

   int getTextLength() const { return GetWindowTextLengthW(hwnd); }
   void setText(const wchar_t* t) { SetWindowTextW(hwnd, t); }
   wchar_t* getText() { InternalGetWindowText(hwnd, buf, 111); return buf; }

   int getClientWidth() { RECT r; GetClientRect(hwnd, &r); return r.right-r.left; }
   int getClientHeight() { RECT r; GetClientRect(hwnd, &r); return r.bottom-r.top; }

   void Show(int mode = 1, bool center = false)
   {
      if(center)
      {
         RECT rhWnd; GetWindowRect(hwnd, &rhWnd);
         if(!hpar) //now, parent is Desktop
         {
            RECT rDesk; SystemParametersInfoA(SPI_GETWORKAREA, 0, &rDesk, 0);
            SetWindowPos(hwnd, nullptr, (rDesk.right-(rhWnd.right-rhWnd.left))/2,
               (rDesk.bottom-(rhWnd.bottom-rhWnd.top))/2, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
         }
         else //parent
         {
            RECT rhPar; GetClientRect(hpar, &rhPar); GetClientRect(hwnd, &rhWnd);
            SetWindowPos(hwnd, hpar, ((rhPar.right-rhPar.left)-(rhWnd.right-rhWnd.left))/2,
               ((rhPar.bottom-rhPar.top)-(rhWnd.bottom-rhWnd.top))/2, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
         }
      }
      ShowWindow(hwnd, mode);
   }

   void setIcon(int id)
   { 
      SendMessageW(hwnd, WM_SETICON, (WPARAM)0, (LPARAM)LoadImageW(hinst, MAKEINTRESOURCEW(id), IMAGE_ICON, 16, 16, 0));
      SendMessageW(hwnd, WM_SETICON, (WPARAM)1, (LPARAM)LoadIconW(hinst, MAKEINTRESOURCEW(id)));
   }

   void Invalidate(bool repaint = false) { InvalidateRect(hwnd, nullptr, repaint); }
   void InvalidateP(bool repaint = false) { InvalidateRect(hpobj->hwnd, nullptr, repaint); }
   void InvalidateHwnd(HWND h, bool repaint = false) { InvalidateRect(h, nullptr, repaint); }

   void SetRedraw(bool redraw) { SendMessageW(hwnd, WM_SETREDRAW, (WPARAM)redraw, NULL); }

protected:

   virtual int OnCreate(WPARAM, LPARAM) { return 0; }
   virtual LONG_PTR OnEraseBkgnd(HDC) { return 0L; }
   virtual int OnPaint(HDC) { return 0; }
   virtual LONG_PTR OnPrintClient(WPARAM, LPARAM) { return 0L; }
   virtual LONG_PTR OnDisplayChange(WPARAM, LPARAM) { return 0L; }
   virtual int OnUserMessage(UINT, WPARAM, LPARAM) { return 0; }
   virtual int OnCommand(WPARAM, LPARAM) { return 0; }
   virtual LONG_PTR OnSize(WPARAM, LPARAM) { return 0L; }
   virtual int OnMciNotify(WPARAM, LPARAM) { return 0; }
   virtual int OnNotify(WPARAM, LPARAM) { return 0; }
   virtual int OnMouseMove(WPARAM, LPARAM) { return 0; }
   virtual int OnMouseLeave() { return 0; }
   virtual int OnLButtonDown(WPARAM, LPARAM) { return 0; }
   virtual int OnLButtonUp(WPARAM, LPARAM) { return 0; }
   virtual int OnRButtonDown(WPARAM, LPARAM) { return 0; }
   virtual int OnRButtonUp(WPARAM, LPARAM) { return 0; }
   virtual int OnMButtonDown(WPARAM, LPARAM) { return 0; }
   virtual int OnMButtonUp(WPARAM, LPARAM) { return 0; }
   virtual int OnMouseWheel(WPARAM, LPARAM) { return 0; }
   virtual LONG_PTR OnTimer(WPARAM, LPARAM) { return 0L; }
   virtual LONG_PTR OnClose() //destroy window and all children
   {
      SetWindowLongW(hwnd, GWLP_USERDATA, (LONG)0); DestroyWindow(hwnd);
      if(!hpar) { PostQuitMessage(0); UnregisterClassW(L"Window", hinst); } //if this is main window -> quit app
      delete this; return 0L;
   }


   //
   LONG_PTR MSG(UINT m, WPARAM wp = 0, LPARAM lp = 0)
   {
      if(m >= WM_USER) return OnUserMessage(m, wp, lp);
      else switch(m)
      {
      case WM_ERASEBKGND: return OnEraseBkgnd(HDC(wp));
      case WM_PAINT: r = OnPaint(BeginPaint(hwnd, &ps)); EndPaint(hwnd, &ps); return r;
      case WM_PRINTCLIENT: return OnPrintClient(wp, lp);
      case WM_DISPLAYCHANGE: return OnDisplayChange(wp, lp);
      case WM_CREATE: return OnCreate(wp, lp);
      case WM_COMMAND: return OnCommand(wp, lp);
      case WM_SIZE: return OnSize(wp, lp);
      case MM_MCINOTIFY: return OnMciNotify(wp, lp);
      case WM_NOTIFY: return OnNotify(wp, lp);
      case WM_MOUSEMOVE: return OnMouseMove(wp, lp);
      case WM_MOUSELEAVE: return OnMouseLeave();
      case WM_LBUTTONDOWN: return OnLButtonDown(wp, lp);
      case WM_LBUTTONUP: return OnLButtonUp(wp, lp);
      case WM_RBUTTONDOWN: return OnRButtonDown(wp, lp);
      case WM_RBUTTONUP: return OnRButtonUp(wp, lp);
      case WM_MBUTTONDOWN: return OnMButtonDown(wp, lp);
      case WM_MBUTTONUP: return OnMButtonUp(wp, lp);
      case WM_MOUSEWHEEL: return OnMouseWheel(wp, lp);
      case WM_TIMER: return OnTimer(wp, lp);
      case WM_CLOSE: case WM_DESTROY: return OnClose();
      }
      return 0;
   }


   //Obj procedure
   static LONG_PTR __stdcall WndProc(HWND h, UINT m, WPARAM wp, LPARAM lp)
   {
      Window* pw = nullptr; if(m == WM_NCCREATE)
      {
         pw = (Window*)((CREATESTRUCTW*)lp)->lpCreateParams;
         SetWindowLongPtrW(h, GWLP_USERDATA, (LONG_PTR)pw); pw->hwnd = h; //attach
      }

      pw = (Window*)GetWindowLongPtrW(h, GWLP_USERDATA);

      if(pw) { LONG_PTR r = pw->MSG(m, wp, lp); if(r) return r; }
      return DefWindowProcW(h, m, wp, lp);
   }
};


//
void Window::Create(Window* pobj, DWORD wces, DWORD wes, DWORD ws, wchar_t* cname, int x, int y, int w, int h, HWND par, int id)
{
   hinst = GetModuleHandleW(nullptr);
   //
   if(!GetClassInfoExW(hinst, cname, &tempwce)) //check if 'Window' is already registered
   {
      WNDCLASSEXW wce = {0};
      wce.cbSize = sizeof(wce);
      wce.hInstance = hinst;
      wce.lpszClassName = cname;
      wce.lpfnWndProc = WndProc;
      wce.hbrBackground = CreateSolidBrush(RGB(222,222,222)); //default fundal
      wce.hCursor = LoadCursorA(nullptr, MAKEINTRESOURCEA(32512));
      wce.style = wces;
      RegisterClassExW(&wce);
   }
   //
   hpobj = pobj;
   hpar = par;
   hid = id;
   if(!par && id) id = 0; //set members of class and precaution for id
   hwnd = CreateWindowExW(wes, cname, nullptr, ws, x, y, w, h, par, (HMENU)hid, hinst, this); //create
}
